//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖保佑             永无BUG 
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//                  不见满街漂亮妹，哪个归得程序员？  


/* ****************************************************************************
 * Author: 671643387
 * Date: 2015/9/15
 * ***************************************************************************/

var util = require('util');
var mysql = require('mysql');
var IPIP = require('ipip').IPIP;
var ServerManager = require('../js/servers.js').ServerManager;

var gServers = new ServerManager();

//var minTimestamp = 1362067200;
//var maxTimestamp = 1443628800;

//var minTimestamp = 1438358400;
//var maxTimestamp = 1441036800;

var minTimestamp = (new Date("2016/01/01 00:00:00").getTime()) / 1000;
var maxTimestamp = (new Date("2016/07/01 00:00:00").getTime()) / 1000;

var MAX_PAY_TOPS = 500;
var MAX_TIME_STAMP = (new Date("2016/07/01 00:00:00").getTime()) / 1000;
var ID_FILE = "id_file.dat";
var LOW_WATER = 2000;
var PAY_SEG = [{
    l:0, h:500
}, {
    l:500, h:1000
}, {
    l:1000, h:5000
}, {
    l:5000, h:10000
}, {
    l:10000, h:0
}];

function main(){
    if (process.argv.length < 3) {
        console.log("usage: program cmd");
        return;
    }


    var cmd = process.argv[2];

    async.series([
        function (cb) {
            gServers.load({slave:true, native:true}, function(err) {
                cb(err);
            });
        },
        function(cb) {
            switch (cmd) {
                case "monthPayNum": // 每月付费人数
                    collect_month_pay_num(cb);
                    break;

                case "monthPayDetail":  // 充值明细
                    collect_month_pay_detail(cb);
                    break;

                case "monthCostCash":   // 每月消费元宝
                    collect_month_cost_cash(cb);
                    break;

                case "monthDetailCash": // 月剩余元宝
                    collect_cash_detail(cb);
                    break;

                case "profit_forecast":
                    collect_profit_forecast(cb);
                    break;

                case "pay_tops":
                    collect_pay_tops(MAX_PAY_TOPS, MAX_TIME_STAMP, cb);
                    break;

                case "pay_detail":
                    export_pay_detail(process.argv[3], MAX_TIME_STAMP, cb);
                    break;

                case "reg_time":
                    export_reg_time(ID_FILE, cb);
                    break;

                case "login_detail":
                    export_login_detail(process.argv[3], MAX_TIME_STAMP, cb);
                    break;

                case "charactor":
                    export_charactor(process.argv[3], cb);
                    break;

                case "lowwater_pay_detail":
                    export_lowwater_pay_detail(LOW_WATER, MAX_TIME_STAMP, cb);
                    break;

                case "pay_seg_num":
                    export_pay_seg_num(PAY_SEG, MAX_TIME_STAMP, cb);
                    break;

                case "all_login_ip":
                    export_all_login_ip(cb);
                    break;

                case "pay_sum_by_sid":
                    export_pay_sum_by_sid(MAX_TIME_STAMP, cb);
                    break;

                case "cash_remain":
                    collect_cash_remain(process.argv[3], MAX_TIME_STAMP, cb);
                    break;

                case "pay_sum_by_sid_series":
                    export_pay_sum_by_sid_series(MAX_TIME_STAMP, cb);
                    break;

                case "export_cash_detail":
                    export_cash_detail(MAX_TIME_STAMP, process.argv[3], cb);
                    break;

                case "export_month_cash_cost":
                    export_month_cash_cost(MAX_TIME_STAMP, cb);
                    break;

                case "exportUserLoginCount":
                    var uidFile = process.argv[3];
                    var startDate = new Date(process.argv[4]).getStamp();
                    var endDate = new Date(process.argv[5]).getStamp();
                    startDate = Date.createFromStamp(startDate).zeroMonth().getStamp();
                    endDate = Date.createFromStamp(Date.nextMonth(endDate)).zeroMonth().getStamp();
                    console.log(util.format("exportUserLoginCount: uidFile=%s, startDate=%d, endDate=%d", uidFile, startDate, endDate));
                    exportUserLoginCount(uidFile, startDate, endDate, cb);
                    break;

                default:
                    console.error("NO SUCH COMMAND");
                    cb();
                    break;
            }
        }
    ], function(err) {
        console.log("finish............................");
        process.exit(0);
    });
}

main();

/**
 * 连接游戏数据库
 * @param db_name   数据库名
 * @param fn        回调函数
 */
function mysql_game_db(db_name, fn) {
    var game_db_config = {
        host : config.mysql.ip,
        port : config.mysql.port,
        user : config.mysql.user,
        password : config.mysql.password,
        database : db_name,
        insecureAuth: true
    };

    console.log(util.format("连接mysql游戏数据库 %j", game_db_config));

    var conn = mysql.createConnection(game_db_config);
    conn.connect(function(err){
        if (err) {
            console.log(util.format("连接mysql游戏数据库出错 %j", err));
            fn();
        } else {
            fn(conn); // 需要在不使用连接的时候手动关闭连接
        }
    });
}

/**
 * 连接GM数据库
 * @param db_name   数据库名
 * @param fn        回调函数
 */
function mysql_gm_db(db_name, fn) {
    var game_db_config = {
        host : config.mysql.gm.ip,
        port : config.mysql.gm.port,
        user : config.mysql.user,
        password : config.mysql.password,
        database : db_name,
        insecureAuth: true
    };

    console.log(util.format("连接mysql GM数据库 %j", game_db_config));

    var conn = mysql.createConnection(game_db_config);
    conn.connect(function(err){
        if (err) {
            console.log(util.format("连接mysql GM数据库出错 %j", err));
            fn();
        } else {
            fn(conn); // 需要在不使用连接的时候手动关闭连接
        }
    });
}

///////////////////////////////////////////////////////////////////////////////

function collect_month_pay_num(fn) {
    console.log("统计每月付费人数");

    var monthPayNum = {};       // 每月付费人数
    async.each(gServers.getSids(), function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        var curTimestamp = minTimestamp;
        async.until(function(){
            return curTimestamp >= maxTimestamp;
        }, function(uncb){
            var nextMonthTimestamp = Date.nextMonth(curTimestamp);

            db.collection("pay").find(
                {
                    "ts":{
                        "$gte":curTimestamp + '',
                        "$lt":nextMonthTimestamp + ''
                    }
                }, {openid:1}).toArray(function(err, res){
                    if (err) {
                        console.log(err);
                    } else {
                        monthPayNum[curTimestamp] = monthPayNum[curTimestamp] || {};

                        res.forEach(function(pay){
                            monthPayNum[curTimestamp][pay.openid] = 1;
                        });
                    }

                    curTimestamp = nextMonthTimestamp;
                    uncb(null);
                }
            );

        }, function(err){
            asecb();
        });
    }, function(err){
        for (var key in monthPayNum) {
            console.log(Date.createFromStamp(+key).format("yyyy-MM-dd") + " " + Object.keys(monthPayNum[key]).length);
        }
        fn(null);
    });
}

function collect_month_pay_detail(fn) {
    console.log("统计每月充值明细");

    var monthPayDetail = {};

    var sidArr = gServers.getSids();
    var compSids = 0;
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        var curTimestamp = minTimestamp;
        async.until(function(){
            return curTimestamp >= maxTimestamp;
        }, function(uncb){
            var nextMonthTimestamp = Date.nextMonth(curTimestamp);

            db.collection("pay").find(
                {
                    "ts":{
                        "$gte":curTimestamp + '',
                        "$lt":nextMonthTimestamp + ''
                    }
                }).toArray(function(err, res){
                    if (err) {
                        console.log(err);
                    } else {
                        monthPayDetail[curTimestamp] = monthPayDetail[curTimestamp] || {amt:0, cash:0, openids:{}, paynum:0};

                        res.forEach(function(pay){
                            monthPayDetail[curTimestamp]["amt"] += +pay.amt;
                            monthPayDetail[curTimestamp]["cash"] += +pay.cash;
                            monthPayDetail[curTimestamp]["openids"][pay.openid] = 1;
                            monthPayDetail[curTimestamp]["paynum"]++;
                        });
                    }

                    curTimestamp = nextMonthTimestamp;
                    uncb(null);
                }
            );

        }, function(err){
            ++compSids;
            console.log(Math.floor((compSids / sidArr.length) * 100) + " %");
            asecb();
        });
    }, function(err){
        console.log("时间,RMB,获得元宝,充值人数,充值笔数");
        for (var key in monthPayDetail) {
            console.log(Date.createFromStamp(+key).format("yyyy-MM")
            + "," + (monthPayDetail[key]["amt"] * config.platform.exchange_rate) + ","
            + monthPayDetail[key]["cash"] + ","
            + Object.keys(monthPayDetail[key]["openids"]).length + ","
            + monthPayDetail[key]["paynum"]);
        }
        fn(null);
    });
}

function collect_month_cost_cash(fn) {
    console.log("统计每月消耗元宝");

    var monthCostCash = {};       // 每月消耗元宝

    var sidArr = gServers.getSids();
    var compSids = 0;
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        var curTimestamp = minTimestamp;
        async.until(function(){
            return curTimestamp >= maxTimestamp;
        }, function(uncb){
            var nextMonthTimestamp = Date.nextMonth(curTimestamp);

            async.series([
                function(icb) {
                    // 查询cash数据库
                    db.collection("cash").find(
                        {
                            "time":{
                                "$gte":curTimestamp,
                                "$lt":nextMonthTimestamp
                            }
                        }, {num:1}).toArray(function(err, res){
                            if (err) {
                                console.log(err);
                            } else if (res.length > 0) {
                                monthCostCash[curTimestamp] = monthCostCash[curTimestamp] || 0;
                                res.forEach(function(cash){
                                    if (cash.num < 0) {
                                        monthCostCash[curTimestamp] += Math.abs(cash.num);
                                    }
                                });
                            }
                            icb(null);
                        }
                    );
                },
                function(icb) {
                    // 查询老的analysis
                    db.collection("analysis").find(
                        {
                            "date":{
                                "$gte":Date.createFromStamp(curTimestamp).stdFormatedString(),
                                "$lt":Date.createFromStamp(nextMonthTimestamp).stdFormatedString()
                            }
                        }, {cash:1}).toArray(function(err, res){
                            if (err) {
                                console.log(err);
                            } else if (res.length > 0) {
                                monthCostCash[curTimestamp] = monthCostCash[curTimestamp] || 0;
                                res.forEach(function(analysis){
                                    monthCostCash[curTimestamp] += analysis.cash;
                                });
                            }
                            icb(null);
                        }
                    );
                }
            ], function(err){
                curTimestamp = nextMonthTimestamp;
                uncb(null);
            });

        }, function(err){
            ++compSids;
            console.log(Math.floor((compSids / sidArr.length) * 100) + " %");
            asecb();
        });

    }, function(err){
        for (var key in monthCostCash) {
            console.log(Date.createFromStamp(+key).format("yyyy-MM-dd") + " " + monthCostCash[key]);
        }
        fn(null);
    });
}

function collect_cash_detail(fn){

    console.log("查询月元宝变更详细");

    var monthDetail = {};
    var monthCashRemain = {};    // 付费玩家月剩余元宝

    var sidArr = gServers.getSids();
    var compSids = 0;
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        var sidPayOpenids = []; // 本服付费OPENID
        var queryUids = []; // 需要统计剩余元宝的角色ID

        var curTimestamp = minTimestamp;

        var uid2openid = {};

        async.series([
            function(icb) {
                // 查询在本服付费的玩家
                db.collection("pay").distinct("openid", {}, {}, function(err, res){
                    if (err) {
                        console.log(err);
                    } else if (res.length > 0) {
                        sidPayOpenids = res;
                    }
                    icb(null);
                });
            },
            function(icb) {
                // 查询付费玩家的UID
                db.collection("plat").find(
                    {
                        "_id":{"$in":sidPayOpenids}
                    }).toArray(function(err, res){
                        if (err) {
                            console.log(err);
                        } else if (res.length > 0) {
                            res.forEach(function(plat){
                                queryUids.push(plat.uid);
                                uid2openid[plat.uid] = plat._id;
                                if (plat.uids) {
                                    plat.uids.forEach(function(uid){
                                        queryUids.push(uid);
                                        uid2openid[uid] = plat._id;
                                    });
                                }
                            });
                        }
                        icb(null);
                    }
                );
            },
            function(icb) {
                // 查询该服务器的付费玩家月剩余元宝
                async.until(function(){
                    return curTimestamp >= maxTimestamp;
                }, function(uncb){
                    var nextMonthTimestamp = Date.nextMonth(curTimestamp);

                    // 查询月最后剩余
                    db.collection('cash').group(
                        {uid:1},
                        {
                            "uid":{
                                "$in":queryUids
                            },
                            "time":{
                                "$gte":curTimestamp,
                                "$lt":nextMonthTimestamp
                            }
                        },
                        {cash:0, time:0, oget:0, tcost:0, pget:0, start_cash:0, start_time:0},
                        function(doc, prev){
                            var docTime = +doc.time;
                            // 统计月末元宝数量
                            if (prev.time == 0 || prev.time < docTime) {
                                prev.time = docTime;
                                prev.cash = doc.remain;
                            }

                            // 统计月初元宝数量
                            if (prev.start_time == 0 || prev.start_time > docTime) {
                                prev.start_time = docTime;
                                prev.start_cash = doc.remain - doc.num;
                            }

                            if (doc.num > 0) {
                                if((doc.mod != "gm" && doc.act != "pay"))
                                    prev.oget += +doc.num;      // 统计非充值获取
                                else
                                    prev.pget += +doc.num;      // 充值获取
                            }

                            // 统计总消耗
                            if (doc.num < 0) {
                                prev.tcost += Math.abs(doc.num);
                            }
                        },
                        function(err, res){
                            if (err) console.log(err);
                            else {
                                monthCashRemain[curTimestamp] = monthCashRemain[curTimestamp] || 0;
                                monthDetail[curTimestamp] = monthDetail[curTimestamp] || [];

                                res.forEach(function(item){
                                    monthCashRemain[curTimestamp] += +item.cash;

                                    monthDetail[curTimestamp].push([
                                        uid2openid[item.uid],
                                        Date.createFromStamp(curTimestamp).format("yyyy-MM"),
                                        sid,
                                        item.uid,
                                        item.start_cash,
                                        item.pget,
                                        item.oget,
                                        item.cash,
                                        item.tcost
                                    ]);
                                });
                            }
                            curTimestamp = nextMonthTimestamp;
                            uncb(null);
                        }
                    );

                }, function(err){
                    icb(null);
                });
            }
        ], function(err){
            ++compSids;
            console.log(Math.floor((compSids / sidArr.length) * 100) + " %");
            asecb();
        });

    }, function(err){
        console.log("openid,时间,SID,UID,上月剩余,充值获得,其他获得,剩余,总消耗");

        for (var key in monthDetail) {
            monthDetail[key].forEach(function(item){
                console.log(item.join(","));
            });
        }

        console.log("==============================================================================");

        for (var key in monthCashRemain) {
            console.log(Date.createFromStamp(+key).format("yyyy-MM-dd") + " " + monthCashRemain[key]);
        }
        fn(null);
    });
}

/**
 * 盈利预测数据导出
 * @param fn
 */
function collect_profit_forecast(fn){
    var sum = {};

    mysql_game_db('login', function(conn){
        if (conn) {
            ///////////////////////////////////////////////////////////////////
            // 按月统计
            var curTimestamp = minTimestamp;
            async.until(function(){
                return curTimestamp >= maxTimestamp;
            }, function(uncb){
                var nextMonthTimestamp = Date.nextMonth(curTimestamp);
                var prevMonthTimestamp = Date.prevMonth(curTimestamp);

                sum[curTimestamp] = sum[curTimestamp] || {};

                console.log(util.format("查询 %s 的数据", Date.createFromStamp(curTimestamp).format("yyyy-MM")));

                var sql = "";
                async.series([
                    // 查询当月总用户数量
                    function(scb) {
                        sql = mysql.format("SELECT COUNT(openid) FROM login WHERE reg_time < ? AND reg_time != 0",
                            [nextMonthTimestamp]);
                        conn.query(sql, function(err, res){
                            var mTUser = 0;
                            if (!err && res && res.length > 0) {
                                mTUser = +res[0]["COUNT(openid)"];
                            }

                            sum[curTimestamp]["total_user"] = mTUser;
                            scb();
                        });
                    },
                    // 查询当月新增用户
                    function(scb) {
                        sql = mysql.format("SELECT COUNT(openid) FROM login WHERE reg_time < ? AND reg_time >= ?",
                            [nextMonthTimestamp, curTimestamp]);
                        conn.query(sql, function(err, res){
                            var mNUser = 0;
                            if (!err && res && res.length > 0) {
                                mNUser = +res[0]["COUNT(openid)"];
                            }

                            sum[curTimestamp]["new_user"] = mNUser;
                            scb();
                        });
                    },
                    // 查询上月新用户留存
                    function(scb) {
                        sql = mysql.format("SELECT COUNT(openid) FROM login WHERE reg_time < ? AND reg_time >= ? AND login_time >= ?",
                            [curTimestamp, prevMonthTimestamp, curTimestamp]);
                        conn.query(sql, function(err, res){
                            var mLNRUser = 0;
                            if (!err && res && res.length > 0) {
                                mLNRUser = +res[0]["COUNT(openid)"];
                            }

                            sum[curTimestamp]["last_new_user_remain"] = mLNRUser;
                            scb();
                        });
                    },
                    // 查询当月活跃用户
                    function(scb) {
                        sql = mysql.format("SELECT COUNT(openid) FROM login WHERE login_time >= ? AND reg_time <= ? AND reg_time != 0",
                            [curTimestamp, nextMonthTimestamp]);
                        conn.query(sql, function(err, res){
                            var mAUser = 0;
                            if (!err && res && res.length > 0) {
                                mAUser = +res[0]["COUNT(openid)"];
                            }

                            sum[curTimestamp]["active_user"] = mAUser;
                            scb();
                        });
                    }
                ], function(err){
                    curTimestamp = nextMonthTimestamp;
                    uncb();
                });

            }, function(err){
                conn.end();

                console.log("时间, 总用户, 新增用户, 上月新用户留存, 月活跃");
                for (var key in sum) {
                    console.log(
                        Date.createFromStamp(+key).format("yyyy-MM") +
                        ',' + sum[key]["total_user"] +
                        ',' + sum[key]["new_user"] +
                        ',' + sum[key]["last_new_user_remain"] +
                        ',' + sum[key]["active_user"]
                    );
                }
                fn();
            });
            ///////////////////////////////////////////////////////////////////
        }
    });
}

/**
 * 查询充值前XX名
 * @param max_tops  需要统计的人数
 * @param max_timestamp 截至时间戳
 * @param fn
 */
function collect_pay_tops(max_tops, max_timestamp, fn){

    var payDetails = {};

    var compSids = 0;
    var sidArr = gServers.getSids();

    // 遍历所有数据库
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");

            ++compSids;
            console.log("complete: " + Math.floor((compSids / sidArr.length) * 100));
            asecb();
            return;
        }

        var condition = {};
        condition.ts = {};
        condition.ts['$lt'] = max_timestamp + '';

        db.collection('pay').mapReduce(function(){
            emit(this.openid, {sum:+this.amt});
        }, function(key, values){
            var result = {sum:0};
            values.forEach(function(value){
                result.sum += value.sum;
            });
            return result;
        }, {
            out:{ inline: 1 },
            query:condition
        }, function(err, res){
            if (err) console.error(err);
            else {
                res.forEach(function(value){
                    payDetails[value._id] = payDetails[value._id] || 0;
                    payDetails[value._id] += +value.value.sum;
                });
            }

            ++compSids;
            console.log("complete: " + Math.floor((compSids / sidArr.length) * 100));
            asecb(null);
        });
    }, function(err){
        var tops = [];

        for (var openid in payDetails) {
            tops.push({
                openid: openid,
                sum: +payDetails[openid]
            });
        }

        tops.sort(function(a, b){
            return (b.sum - a.sum);
        });

        for (var i = 0; i < max_tops; ++i) {
            console.log( util.format( "%d, %s, %d", i + 1, tops[i]["openid"],
                tops[i]["sum"] * config.platform.exchange_rate ) );
        }
    });
}

/**
 * 导出充值明细
 * @param idfile    ID文件路径(文件中每行一个openid)
 * @param fn
 */
function export_pay_detail(idfile, max_timestamp, fn){
    console.log("================ process " + idfile);

    var openids = [];
    var paySum = [];

    async.series([
        function(cb) {
            var data = fs.readFileSync(idfile, 'UTF-8');
            openids = data.split('\n');
            console.log(openids.length + " openid");
            cb(null);
        },
        function(cb) {
            async.each(gServers.getSids(), function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                db.collection('pay').find({
                    openid:{"$in":openids},
                    ts:{"$lt": max_timestamp + ''}
                }).toArray(function(err, res){
                    if (err) {
                        console.error(sid + " plat failed " + err);
                        asecb();
                    } else {
                        paySum = paySum.concat(res);
                        asecb(null);
                    }
                });

            }, function(err){
                cb(null);
            });
        },
        function(cb) {

            console.log(util.format("openid,billno,gid,cash,ts,time,金额,状态,amt"));
            paySum.forEach(function(pay){
                console.log(util.format("%s,%s,%s,%d,%d,%s,%d,%d,%d",
                pay.openid, pay.billno, pay.gid, +pay.cash, +pay.ts, pay.time,
                    (+pay.amt) * config.platform.exchange_rate, +pay.state, +pay.amt));
            });

            cb();
        }
    ], function(err) {
        console.log("================ finish process file " + idfile);
        fn();
    });
}

/**
 * 导出注册时间
 * @param idfile    ID文件路径(文件中每行一个openid)
 * @param fn
 */
function export_reg_time(idfile, fn){
    console.log("================ process " + idfile);

    var openids = [];

    async.series([
        function(cb) {
            var data = fs.readFileSync(idfile, 'UTF-8');
            openids = data.split('\n');
            for (var key in openids) {
                openids[key] = "'" + openids[key] + "'";
            }

            console.log(openids.length + " openid");
            cb(null);
        },
        function(cb) {
            mysql_game_db('login', function(conn){
                if (conn) {
                    var sql = "SELECT openid, reg_time, login_time FROM login WHERE openid IN (" + openids.join(',') + ");";
                    console.log("openid,注册时间,最后登录时间");
                    conn.query(sql, function(err, res){
                        if (!err && res && res.length > 0) {
                            res.forEach(function(item){
                                console.log(util.format("%s, %s, %s", item["openid"],
                                    Date.createFromStamp(+item["reg_time"]).stdFormatedString(),
                                    Date.createFromStamp(+item["login_time"]).stdFormatedString()));
                            });
                        }
                        conn.end();
                        cb();
                    });
                } else
                    cb();
            });
        }
    ], function(err) {
        console.log("================ finish process file " + idfile);
        fn();
    });
}

/**
 * 导出登录明细
 * @param idfile
 * @param max_timestamp 截至时间戳
 * @param fn
 */
function export_login_detail(idfile, max_timestamp, fn){
    console.log("================ process " + idfile);

    var openids = [];
    var loginSum = {};

    var monthLogin = {};

    var UAD = {}; // 活跃天数

    var uid_2_openid = {};

    mysql_gm_db('stat', function(conn){
        if (conn) {
            async.series([
                function(cb) {
                    var data = fs.readFileSync(idfile, 'UTF-8');
                    openids = data.split('\n');
                    console.log(openids.length + " openid");
                    cb(null);
                },
                function(cb) {
                    async.each(gServers.getSids(), function(sid, asecb) {
                        var db = gServers.getGameDB(sid);
                        if (!db) {
                            console.log(sid + " db error");
                            asecb();
                            return;
                        }

                        console.log("查询 " + sid);

                        var uids = [];
                        async.series([
                            // 查询UID
                            function(cb2) {
                                db.collection('plat').find({
                                    _id:{"$in":openids}
                                }).toArray(function(err, res){
                                    if (err) {
                                        console.error(sid + " plat failed " + err);
                                        cb2();
                                    } else {

                                        res.forEach(function(plat){
                                            var platUids = plat.uids || [];
                                            if (plat.uid && (platUids.indexOf(plat.uid) == -1)) {
                                                platUids.push(plat.uid);
                                            }
                                            if (platUids.length > 0) {
                                                uids = uids.concat(platUids);
                                                platUids.forEach(function(uid){
                                                    uid_2_openid[uid] = plat._id;
                                                });
                                            }
                                        });

                                        cb2(null);
                                    }
                                });
                            },

                            function(cb2) {
                                for (var key in uids) {
                                    uids[key] = "'" + uids[key] + "'";
                                }
                                var sql = "SELECT time, uid, sid, times FROM login WHERE uid IN (" + uids.join(',') + ") AND time < " + max_timestamp + " AND time >= " + minTimestamp + ";";
                                console.log(sql);
                                conn.query(sql, function(err, res){
                                    if (!err && res && res.length > 0) {
                                        res.forEach(function(item){
                                            var uid = +item.uid;
                                            var openid = uid_2_openid[uid];
                                            loginSum[openid] = loginSum[openid] || [];
                                            loginSum[openid].push(item);

                                            // 统计玩家按月登录次数
                                            var time = +item.time;
                                            var mStart = Date.createFromStamp(time).zeroMonth().getStamp();
                                            monthLogin[uid] = monthLogin[uid] || {};
                                            monthLogin[uid][mStart] = monthLogin[uid][mStart] || 0;
                                            monthLogin[uid][mStart] += +item.times;

                                            // 统计活跃天数
                                            UAD[uid] = UAD[uid] || 0;
                                            UAD[uid]++;
                                        });
                                    }
                                    cb2();
                                });
                            }
                        ], function(err){
                            asecb();
                        });
                    }, function(err){
                        cb(null);
                    });
                },
                function(cb) {

                    console.log(util.format("openid,uid,time,登录次数"));
                    for (var openid in loginSum) {
                        var userSum = loginSum[openid];
                        userSum.forEach(function(log){
                            console.log(util.format("%s,%d,%s,%d",
                                    openid, +log.uid, Date.createFromStamp(+log["time"]).format("yyyy-MM-dd"), +log.times)
                            );
                        });
                    }

                    console.log("===================================================");

                    console.log("openid,uid,月份,登录次数");
                    for (var uid in monthLogin) {
                        var uidM = monthLogin[uid];
                        for (var m in uidM) {
                            var openid = uid_2_openid[uid];
                            console.log(util.format("%s, %d, %s, %d",
                                openid, uid, Date.createFromStamp(+m).format("yyyy-MM"),
                            +uidM[m]));
                        }
                    }

                    console.log("===================================================");
                    console.log("openid,uid,活跃天数");
                    for (var uid in UAD) {
                        console.log(util.format("%s, %d, %d",
                        uid_2_openid[uid], +uid, +UAD[uid]));
                    }

                    cb();
                }
            ], function(err) {
                console.log("================ finish process file " + idfile);
                conn.end();
                fn();
            });
        }
    });
}

/**
 * 导出角色
 * @param idfile
 * @param fn
 */
function export_charactor(idfile, fn){

    var openids;
    var chaSum = {};

    async.series([
        function(cb) {
            var data = fs.readFileSync(idfile, 'UTF-8');
            openids = data.split('\n');
            console.log(openids.length + " openid");
            cb(null);
        },
        function(cb) {
            async.each(gServers.getSids(), function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                console.log("查询 " + sid);

                var uid_2_openid = {};
                var uids = [];
                async.series([
                    // 查询UID
                    function(cb2) {
                        db.collection('plat').find({
                            _id:{"$in":openids}
                        }).toArray(function(err, res){
                            if (err) {
                                console.error(sid + " plat failed " + err);
                                cb2();
                            } else {

                                res.forEach(function(plat){
                                    if (plat.uids) {
                                        uids = uids.concat(plat.uids);
                                        plat.uids.forEach(function(uid){
                                            uid_2_openid[uid] = plat._id;
                                        });
                                    } else {
                                        uids.push(plat.uid);
                                        uid_2_openid[plat.uid] = plat._id;
                                    }
                                });

                                cb2(null);
                            }
                        });
                    },
                    // 查询角色数据
                    function(cb2) {
                        db.collection("user").find({_id:{"$in":uids}}).toArray(function(err, res){
                            if (!err && res && res.length > 0) {
                                res.forEach(function(user){
                                    var openid = uid_2_openid[user._id];
                                    chaSum[openid] = chaSum[openid] || [];
                                    user.sid = sid;
                                    chaSum[openid].push(user);
                                });
                            }
                            cb2();
                        });
                    }
                ], function(err){
                    asecb();
                });
            }, function(err){
                cb(null);
            });
        }
    ], function(err) {

        console.log("openid, uid, 区服,合区后真实区服,角色名,等级,注册IP,注册时间,最后登录时间,总在线时长");
        for (var openid in chaSum) {
            chaSum[openid].forEach(function(cha){
                console.log(util.format("%s, %d, %d, %d, %s, %d, %s, %s, %s", openid,
                    cha._id, _getOriginSID(cha._id), cha.sid,
                    cha.info.un, cha.status.level, cha.info.createIP || "",
                    cha.info.create, cha.mark.day, cha.mark.online_time));
            });
        }

        console.log("================ finish process file " + idfile);
        fn();
    });
}

/**
 * 导出充值金额大于等于low_water明细
 * @param low_water    最低数据
 * @param max_timestamp
 * @param fn
 */
function export_lowwater_pay_detail(low_water, max_timestamp, fn){
    var paySum = [];

    var minAMT = Math.floor(low_water / config.platform.exchange_rate);

    async.series([
        function(cb) {
            async.each(gServers.getSids(), function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                db.collection('pay').find({
                    ts:{"$lt": max_timestamp + ''}
                }).toArray(function(err, res){
                    if (err) {
                        console.error(sid + " plat failed " + err);
                        asecb();
                    } else {
                        res.forEach(function(pay){
                            if (+pay.amt >= minAMT) {
                                paySum.push(pay);
                            }
                        });
                        asecb(null);
                    }
                });

            }, function(err){
                cb(null);
            });
        },
        function(cb) {

            console.log(util.format("openid,billno,gid,cash,ts,time,金额,状态,amt"));
            paySum.forEach(function(pay){
                console.log(util.format("%s,%s,%s,%d,%d,%s,%d,%d,%d",
                    pay.openid, pay.billno, pay.gid, +pay.cash, +pay.ts, pay.time,
                    (+pay.amt) * config.platform.exchange_rate, +pay.state, +pay.amt));
            });

            cb();
        }
    ], function(err) {
        console.log("================ finish");
        fn();
    });
}

/**
 * 导出区间充值玩家数量
 * @param payseg
 * @param max_timestamp
 * @param fn
 */
function export_pay_seg_num(payseg, max_timestamp, fn){
    var segSum = {};

    var compSid = 0;
    var sidArr = gServers.getSids();
    async.series([
        function(cb) {
            async.each(sidArr, function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                db.collection('pay').find({
                    ts:{"$lt": max_timestamp + ''}
                }).toArray(function(err, res){
                    if (err) {
                        console.error(sid + " plat failed " + err);
                        asecb();
                    } else {
                        res.forEach(function(pay){

                            for (var x = 0; x < payseg.length; ++x) {
                                var seg = payseg[x];
                                var payRMB = (+pay.amt) * config.platform.exchange_rate;
                                if (seg.h != 0) {
                                    if (payRMB >= seg.l && payRMB < seg.h) {
                                        segSum[x] = segSum[x] || 0;
                                        segSum[x] = segSum[x] + 1;
                                        break;
                                    }
                                } else {
                                    if (payRMB >= seg.l) {
                                        segSum[x] = segSum[x] || 0;
                                        segSum[x] = segSum[x] + 1;
                                        break;
                                    }
                                }
                            }
                        });
                        ++compSid;
                        console.log("complete " + Math.floor((compSid / sidArr.length) * 100));
                        asecb(null);
                    }
                });

            }, function(err){
                cb(null);
            });
        }
    ], function(err) {
        // 输出表头
        var headLine = "";
        for (var x = 0; x < payseg.length; ++x) {
            headLine += util.format("%d-%d", payseg[x].l, payseg[x].h);
            if ((x + 1) < payseg.length) {
                headLine += ",";
            }
        }
        console.log(headLine);

        // 输出数据
        var dataLine = "";
        for (var x = 0; x < payseg.length; ++x) {
            if (segSum[x]) {
                dataLine += util.format("%d", segSum[x]);
            } else {
                dataLine += "0";
            }

            if ((x + 1) < payseg.length) {
                dataLine += ",";
            }
        }
        console.log(dataLine);

        console.log("================ finish");
        fn();
    });
}

/**
 * 导出所有玩家登录IP
 * @param fn
 */
function export_all_login_ip(fn){
    var ip = new IPIP();
    mysql_game_db('login', function(conn){
        if (conn) {
            var sql = "SELECT openid, login_ip FROM login WHERE login_ip IS NOT NULL;";
            console.log("openid,最近登录IP,省份");
            conn.query(sql, function(err, res){
                if (!err && res && res.length > 0) {
                    res.forEach(function(item){
                        var ipDetails = {};
                        try {
                            ipDetails = ip.ip(item["login_ip"]) || {};
                        } catch (e){}
                        console.log(util.format("%s, %s, %s", item["openid"],
                            item["login_ip"], ipDetails["province"]));
                    });
                }
                conn.end();
                fn();
            });
        } else
            fn();
    });
}

/**
 * 导出充值数据( 总充值|总充值次数|总充值人数|服务器充值人数 )
 * @param max_timestamp
 * @param fn
 */
function export_pay_sum_by_sid(max_timestamp, fn){

    var payRMBSum = 0;  // 总充值
    var payCountSum = 0; // 总充值次数
    var payOpenids = {}; // 所有充值用户
    var payOpenidsBySid = {}; // 服务器充值用户
    var payRMBBySid = {}; // 服务器充值金额

    var compSids = 0;
    var sidArr = gServers.getSids();
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        db.collection('pay').find({
            ts:{"$lt": max_timestamp + ''}
        }).toArray(function(err, res){
            if (err) {
                console.error(sid + " pay failed " + err);
                asecb();
            } else {

                payRMBBySid[sid] = payRMBBySid[sid] || 0;
                payOpenidsBySid[sid] = payOpenidsBySid[sid] || {};

                payCountSum += res.length;
                res.forEach(function(pay){
                    var orderRMB = (+pay.amt);
                    payRMBSum += orderRMB;
                    payRMBBySid[sid] += orderRMB;
                    payOpenids[pay.openid] = 1;
                    payOpenidsBySid[sid][pay.openid] = 1;
                });

                compSids++;
                console.log("complete " + Math.floor((compSids / sidArr.length) * 100));
                asecb(null);
            }
        });

    }, function(err){
        console.log("RMB " + (payRMBSum * config.platform.exchange_rate));
        console.log("付费次数 " + payCountSum);
        console.log("付费人数 " + Object.keys(payOpenids).length);

        console.log("sid,付费人数,付费金额");
        for (var sid in payOpenidsBySid) {
            console.log(util.format("%d, %d, %d", +sid,
                Object.keys(payOpenidsBySid[sid]).length,
                (payRMBBySid[sid] * config.platform.exchange_rate)));
        }

        fn(null);
    });
}

/**
 * 查询付费玩家剩余元宝
 * @param idfile
 * @param max_timestamp
 * @param fn
 */
function collect_cash_remain(idfile, max_timestamp, fn){

    var monthCashRemain = {};    // 付费玩家月剩余元宝
    var historyRemain = {};         // 历史记录（在cash表不存在）
    var payOpenids = {};        // 付费openid表

    var openids = [];

    if (idfile != "") {
        var data = fs.readFileSync(idfile, 'UTF-8');
        openids = data.split('\n');
        console.log(openids.length + " openid");
    }

    var sidArr = gServers.getSids();
    var compSids = 0;
    async.each(sidArr, function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        var sidPayOpenids = []; // 本服付费OPENID
        var queryUids = []; // 需要统计剩余元宝的角色ID

        var curTimestamp = minTimestamp;

        var uidmap = {};
        var uid2openid = {};
        async.series([
            function(icb) {
                if (idfile != "") {
                    openids.forEach(function(openid){
                        sidPayOpenids.push(openid);
                        payOpenids[openid] = 1;
                    });
                    icb();
                    return;
                }
                // 查询在本服付费的玩家
                db.collection('pay').find({
                    ts:{"$lt": max_timestamp + ''}
                }).toArray(function(err, res){
                    if (err) {
                        console.error(sid + " pay failed " + err);
                    } else {
                        var openids = {};
                        res.forEach(function(pay){
                            openids[pay.openid] = 1;
                            payOpenids[pay.openid] = 1;
                        });
                        sidPayOpenids = Object.keys(openids);
                    }
                    icb(null);
                });
            },
            function(icb) {
                // 查询付费玩家的UID
                db.collection("plat").find(
                    {
                        "_id":{"$in":sidPayOpenids}
                    }).toArray(function(err, res){
                        if (err) {
                            console.log(err);
                        } else if (res.length > 0) {
                            var usedOpenids = {};
                            res.forEach(function(plat){
                                if (plat.uids) {
                                    plat.uids.forEach(function(uid){
                                        queryUids.push(uid);
                                        uid2openid[uid] = plat._id;
                                        uidmap[uid] = 1;
                                        usedOpenids[plat._id] = 1;
                                    });
                                } else {
                                    queryUids.push(plat.uid);
                                    uid2openid[plat.uid] = plat._id;
                                    uidmap[plat.uid] = 1;
                                    usedOpenids[plat._id] = 1;
                                }
                            });

                            console.log(util.format("%d 用户没有找到UID, %d, %d",
                            sidPayOpenids.length - Object.keys(usedOpenids).length,
                                sidPayOpenids.length, Object.keys(usedOpenids).length));
                        }
                        icb(null);
                    }
                );
            },
            function(icb) {
                // 查询该服务器的付费玩家月剩余元宝
                async.until(function(){
                    return curTimestamp >= maxTimestamp;
                }, function(uncb){
                    var nextMonthTimestamp = Date.nextMonth(curTimestamp);

                    // 查询月最后剩余
                    db.collection('cash').group(
                        {uid:1},
                        {
                            "uid":{
                                "$in":queryUids
                            },
                            "time":{
                                "$gte":curTimestamp,
                                "$lt":nextMonthTimestamp
                            }
                        },
                        {cash:0, time:0},
                        function(doc, prev){
                            var docTime = +doc.time;
                            // 统计月末元宝数量
                            if (prev.time == 0 || prev.time < docTime) {
                                prev.time = docTime;
                                prev.cash = doc.remain;
                            }
                        },
                        function(err, res){
                            if (err) console.log(err);
                            else {
                                res.forEach(function(item){
                                    var openid = uid2openid[item.uid];
                                    monthCashRemain[openid] = monthCashRemain[openid] || {};
                                    monthCashRemain[openid][curTimestamp] = monthCashRemain[openid][curTimestamp] || 0;
                                    monthCashRemain[openid][curTimestamp] += +item.cash;

                                    delete uidmap[item.uid];
                                });
                            }
                            curTimestamp = nextMonthTimestamp;
                            uncb(null);
                        }
                    );

                }, function(err){
                    icb(null);
                });
            },
            // 查询cash中没有记录的玩家数据
            function(icb) {
                var uidremain = Object.keys(uidmap);
                for (var key in uidremain) {
                    uidremain[key] = +uidremain[key];
                }

                console.log("uidremain " + uidremain.length);
                db.collection("user").find({_id:{"$in":uidremain}}).toArray(function(err, res){
                    if (!err && res && res.length > 0) {
                        res.forEach(function(user){
                            var openid = uid2openid[user._id];
                            var cash = +user.status.cash;

                            historyRemain[openid] = historyRemain[openid] || 0;
                            historyRemain[openid] += cash;

                            delete uidmap[user._id];
                        });
                    }
                    console.log("%d UID没有找到角色数据", Object.keys(uidmap).length);
                    icb();
                });
            }
        ], function(err){
            ++compSids;
            console.log(Math.floor((compSids / sidArr.length) * 100) + " %");
            asecb();
        });

    }, function(err){

        console.log("总充值人数：" + Object.keys(payOpenids).length);

        console.log("openid, 时间, 剩余元宝");
        for (var openid in monthCashRemain) {
            delete payOpenids[openid];

            var um = monthCashRemain[openid];
            for (var m in um) {
                console.log( util.format("%s, %s, %d", openid,
                    Date.createFromStamp(+m).format("yyyy-MM"),
                +um[m]) );
            }
        }

        // 结束月
        var endMonthStamp = Date.prevMonth(maxTimestamp);

        console.log("===========================================================");

        console.log("openid, 时间, 剩余元宝");
        for (var openid in monthCashRemain) {
            var um = monthCashRemain[openid];
            var _m = 0;
            for (var m in um) {
                _m = +m;
                if (_m >= endMonthStamp) {
                    break;
                }
            }

            var hCash = 0;
            if (historyRemain[openid]) {
                hCash = +historyRemain[openid];
                delete historyRemain[openid];
            }

            console.log( util.format("%s, %s, %d", openid,
                Date.createFromStamp(+_m).format("yyyy-MM"),
                +um[_m] + hCash) );
        }

        for (var openid in historyRemain) {
            delete payOpenids[openid];

            console.log( util.format("%s, %s, %d", openid,
                Date.createFromStamp(Date.prevMonth(maxTimestamp)).format("yyyy-MM"),
                +historyRemain[openid]) );
        }

        console.log(payOpenids);

        fn(null);
    });
}

function export_pay_sum_by_sid_series(max_timestamp, fn){

    var payRMBSum = 0;  // 总充值
    var payCountSum = 0; // 总充值次数
    var payOpenids = {}; // 所有充值用户
    var payOpenidsBySid = {}; // 服务器充值用户
    var payRMBBySid = {}; // 服务器充值金额

    async.eachSeries(gServers.getSids(), function(sid, escb){
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            escb();
            return;
        }

        db.collection('pay').find({
            ts:{"$lt": max_timestamp + ''}
        }).toArray(function(err, res){
            if (err) {
                console.error(sid + " pay failed " + err);
                escb();
            } else {

                payRMBBySid[sid] = payRMBBySid[sid] || 0;
                payOpenidsBySid[sid] = payOpenidsBySid[sid] || {};

                payCountSum += res.length;
                res.forEach(function(pay){
                    var orderRMB = (+pay.amt);
                    payRMBSum += orderRMB;
                    payRMBBySid[sid] += orderRMB;
                    payOpenids[pay.openid] = 1;
                    payOpenidsBySid[sid][pay.openid] = 1;
                });

                console.log((payRMBSum * config.platform.exchange_rate) + "," + payCountSum + "," + Object.keys(payOpenids).length + ","
                + sid + "," + Object.keys(payOpenidsBySid[sid]).length + "," + (payRMBBySid[sid] * config.platform.exchange_rate));

                escb(null);
            }
        });
    }, function(err){
        console.log("RMB " + (payRMBSum * config.platform.exchange_rate));
        console.log("付费次数 " + payCountSum);
        console.log("付费人数 " + Object.keys(payOpenids).length);

        console.log("sid,付费人数,付费金额");
        for (var sid in payOpenidsBySid) {
            console.log(util.format("%d, %d, %d", +sid,
                Object.keys(payOpenidsBySid[sid]).length,
                (payRMBBySid[sid] * config.platform.exchange_rate)));
        }

        fn(null);
    });
}

/**
 * 导出指定玩家的元宝变更记录(cash)
 * @param max_timestamp 记录最大时间戳
 * @param idfile 玩家openid文件，每行一个openid
 * @param fn
 */
function export_cash_detail(max_timestamp, idfile,fn) {
    console.log("================ process " + idfile);

    var openids = [];
    var cashSum = [];

    var monthCashSum = {};

    async.series([
        function(cb) {
            var data = fs.readFileSync(idfile, 'UTF-8');
            openids = data.split('\n');
            console.log(openids.length + " openid");
            cb(null);
        },
        function(cb) {
            async.each(gServers.getSids(), function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                var uids = [];
                var uid_2_openid = {};

                async.series([
                    function(scb) {
                        db.collection('plat').find({
                            _id:{"$in":openids}
                        }).toArray(function(err, res){
                            if (err) {
                                console.error(sid + " plat failed " + err);
                                scb();
                            } else {

                                res.forEach(function(plat){
                                    if (plat.uids) {
                                        plat.uids.forEach(function(uid){
                                            uid_2_openid[uid] = plat._id;
                                            uids.push(+uid);
                                        });
                                    } else {
                                        uids.push(+plat.uid);
                                        uid_2_openid[plat.uid] = plat._id;
                                    }
                                });

                                scb(null);
                            }
                        });
                    },
                    function(scb) {
                        db.collection('cash').find({
                            uid:{"$in":uids},
                            time:{"$lt": max_timestamp}
                        }).toArray(function(err, res){
                            if (err) {
                                console.error(sid + " plat failed " + err);
                                asecb();
                            } else {

                                res.forEach(function(cash){

                                    var openid = uid_2_openid[cash.uid];
                                    cashSum.push([
                                        openid,
                                        cash.uid,
                                        cash.mod,
                                        cash.act,
                                        cash.num,
                                        cash.remain,
                                        cash.level,
                                        cash.arg1,
                                        cash.arg2,
                                        Date.createFromStamp(+cash.time).stdFormatedString()
                                    ]);

                                    // 统计当月元宝消耗
                                    var monthStamp = Date.createFromStamp(+cash.time).zeroMonth().getStamp();
                                    monthCashSum[monthStamp] = monthCashSum[monthStamp] || 0;
                                    if (cash.num < 0) {
                                        monthCashSum[monthStamp] += Math.abs(cash.num);
                                    }

                                });

                                scb(null);
                            }
                        });
                    }
                ], function(err){
                    asecb();
                });
            }, function(err){
                cb(null);
            });
        },
        function(cb) {

            console.log("openid, uid, mod, act, 元宝变化值, 剩余元宝, 玩家等级, arg1, arg2, 时间");

            cashSum.forEach(function(item){
                console.log(item.join(','));
            });

            console.log("===========================");
            console.log("时间,消耗元宝");
            for (var m in monthCashSum) {
                console.log( util.format( "%s, %d",
                    Date.createFromStamp(+cash.time).format("yyyy-MM"), +monthCashSum[m] ) );
            }

            cb();
        }
    ], function(err) {
        console.log("================ finish process file " + idfile);
        fn();
    });
}

function export_month_cash_cost(max_timestamp,fn) {
    console.log("================ process ");

    var monthCashSum = {};

    async.series([
        function(cb) {
            async.each(gServers.getSids(), function(sid, asecb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    console.log(sid + " db error");
                    asecb();
                    return;
                }

                db.collection('cash').find({
                    time:{"$lt": max_timestamp}
                }).toArray(function(err, res){
                    if (err) {
                        console.error(sid + " plat failed " + err);
                    } else {
                        res.forEach(function(cash){
                            // 统计当月元宝消耗
                            if (cash.num < 0) {
                                var monthStamp = Date.createFromStamp(+cash.time).zeroMonth().getStamp();
                                monthCashSum[monthStamp] = monthCashSum[monthStamp] || 0;

                                monthCashSum[monthStamp] += Math.abs(cash.num);
                            }

                        });
                    }

                    asecb();
                });

            }, function(err){
                cb(null);
            });
        },
        function(cb) {

            console.log("时间,消耗元宝");
            for (var m in monthCashSum) {
                console.log( util.format( "%s, %d",
                    Date.createFromStamp(+m).format("yyyy-MM"), +monthCashSum[m] ) );
            }

            cb();
        }
    ], function(err) {
        console.log("================ finish process file ");
        fn();
    });
}

// 导出玩家登录次数
function exportUserLoginCount(uidFile, startDate, endDate, fn) {
    var strFile = fs.readFileSync(uidFile, 'UTF-8');
    var originUids = strFile.split('\n');
    for (var i = 0; i < originUids.length; ++i) {
        originUids[i] = +originUids[i];
    }

    var tempRes = {};

    async.each(gServers.getSids(), function(sid, asecb) {
        var db = gServers.getGameDB(sid);
        if (!db) {
            console.log(sid + " db error");
            asecb();
            return;
        }

        db.collection('log').find({
            uid:{"$in":originUids},
            sub:{"$gte":startDate, "$lt":endDate}
        }).toArray(function(err, res){
            if (err) {
                console.error(sid + " plat failed " + err);
                asecb();
            } else {

                res.forEach(function(login){
                    var uid = +login.uid;
                    var zeroedMonth = Date.createFromStamp(+login.sub).zeroMonth().getStamp();
                    var uidRes = tempRes[uid] || {};
                    var monthLoginCount = uidRes[zeroedMonth] || 0;
                    monthLoginCount += +login.times;
                    uidRes[zeroedMonth] = monthLoginCount;
                    tempRes[uid] = uidRes;
                });

                asecb(null);
            }
        });

    }, function(err){
        var startMonth = startDate;
        var endMonth = endDate;
        var strHead = "";
        for (var month = startMonth; month < endMonth; month = Date.nextMonth(month)) {
            strHead += Date.createFromStamp(month).format("yyyy-MM") + ",";
        }
        console.log(strHead);

        for (var uid in tempRes) {
            if (!tempRes.hasOwnProperty(uid)) {
                continue;
            }

            var uidRes = tempRes[uid];
            var strLine = "" + uid + ",";
            for (month = startMonth; month < endMonth; month = Date.nextMonth(month)) {
                strLine += (uidRes[month] || 0) + ",";
            }
            console.log(strLine);
        }

        fn();
    });
}

///////////////////////////////////////////////////////////////////////////////

function _getOriginSID(uid) {
    var sid = Math.floor(uid/1000000);
    if (uid > 100000000) {
        sid = Math.floor((uid-100000000)/100000);
    }
    return sid - 1;
}
